﻿using System;
using System.Collections.Generic;
////using System.Linq;
using System.Text;
using System.Threading;
using DBConnectionLib;

namespace DBSourceToolsLib
{
    public abstract class ProcessRunner
    {
        private Thread m_Thread;

        public event EventHandler<ProgressEventArgs> ProgressEventPublisher;

        //protected Database m_Database;
        System.Diagnostics.Process m_Process;
        private string m_OutputFileName;
        private bool m_MustReturnRunTime;
        private bool m_UsingStreams;
        
        public bool UsingStreams
        {
            get { return m_UsingStreams; }
            set { m_UsingStreams = value; }
        }
        DateTime m_dtStart;

        public abstract string GetFileName();
        public abstract string GetArguments();
        public abstract string GetOutputFileName();
        public abstract string GetWorkingDir();
        public abstract string GetTempFileName();

        //public abstract string GetSQLScript();
        //public abstract string GetScriptName();
        
        public ProcessRunner(bool returnRunningTime)
        {
            //this.m_Database = oDatabase;
            this.m_MustReturnRunTime = returnRunningTime;
        }

        public void RunUsingFile()
        {
            m_UsingStreams = false;
            m_Thread = null;
            m_Thread = new Thread(new ThreadStart(this.ThreadFunc));
            // delete output files
            //RemoveFile(this.GetOutputFileName());
            // create script files
            CreateSQLFile();

            // start the process
            this.m_Process = null;
            m_Process = new System.Diagnostics.Process();
            // Redirect the output stream of the child process.
            m_Process.StartInfo.UseShellExecute = false;
            m_Process.EnableRaisingEvents = true;
            m_Process.StartInfo.CreateNoWindow = true;

            m_Process.StartInfo.FileName = GetFileName();
            m_Process.StartInfo.Arguments = GetArguments();
            
            m_Thread.Start();
        }

        private void CreateSQLFile()
        {
            
            //if (!System.IO.Directory.Exists("temp"))
            //{
            //    System.IO.Directory.CreateDirectory("temp");
            //}
            //// create a tempScript with this query
            //using (System.IO.StreamWriter oStreamWriter
            //    = new System.IO.StreamWriter("temp\\" + this.GetScriptName() + ".sql"))
            //{
            //    oStreamWriter.Write(this.GetSQLScript());
            //}

        }

        //private void RemoveFile(string strFileName)
        //{
        //    if (System.IO.File.Exists(strFileName))
        //    {
        //        System.IO.File.Delete(strFileName); 
        //    }
        //}

        private void ThreadFunc()
        {
            EventHandler<ProgressEventArgs> localHandler = ProgressEventPublisher;
            this.m_dtStart = DateTime.Now;
            try
            {
                if (localHandler != null)
                    localHandler(this, new ProgressEventArgs(0, 0,
                        "Executing " + this.GetFileName() + " ... " + Environment.NewLine));

                m_Process.Start();
                if (this.m_UsingStreams)
                {
                    System.Console.WriteLine("using streams, begin reading");
                    m_Process.BeginErrorReadLine();
                    m_Process.BeginOutputReadLine();
                }
                

                while (this.m_Process.WaitForExit(1000) == false)
                    ;
                //this.m_Process.WaitForExit();
                
                //this.m_Process.Close();

            }
            catch (Exception ex)
            {
                if (localHandler != null)
                    localHandler(this, new ProgressEventArgs(0, 0,
                        "Thread Threw Exception " + ex.ToString()));

            }

            finally
            {
                if (this.m_MustReturnRunTime)
                {
                    DateTime dtEnd = DateTime.Now;
                    TimeSpan oTimeSpan = dtEnd - m_dtStart;
                    if (localHandler != null)
                        localHandler(this, new ProgressEventArgs(100, 100,
                            "Run Time : " + oTimeSpan.ToString()));
                }
                if (!String.IsNullOrEmpty( GetTempFileName()))
                    if (System.IO.File.Exists(GetTempFileName()))
                        System.IO.File.Delete(GetTempFileName());

            }

        }

        public void Join()
        {
            string strResults = "";
            
            if (this.m_Process != null)
            {
                try
                {
                    this.m_Process.WaitForExit();
                }
                catch (Exception ex)
                {
                    // thread has stopped
                }
            }
            this.m_Thread.Join();
            EventHandler<ProgressEventArgs> localHandler = ProgressEventPublisher;
            if (System.IO.File.Exists(this.GetOutputFileName()))
            {

                using (System.IO.StreamReader oStreamReader = new System.IO.StreamReader(
                    this.GetOutputFileName()))
                {
                    strResults += oStreamReader.ReadToEnd();
                }
            }
            if (localHandler != null)
                localHandler(this, new ProgressEventArgs(100, 100,
                    strResults));

        }

        public void Abort()
        {

            EventHandler<ProgressEventArgs> localHandler = ProgressEventPublisher;

            try
            {
                this.m_Process.Kill();
            }
            catch (Exception ex)
            {
                if (localHandler != null)
                    localHandler(this, new ProgressEventArgs(0, 0,
                        "Thread Aborted with message :" + ex.ToString()));

            }
            if (this.m_Thread != null)
            {
                if (this.m_Thread.ThreadState == ThreadState.Running)
                {
                    this.m_Thread.Abort();
                    this.m_Thread.Join();
                }

                if (localHandler != null)
                    localHandler(this, new ProgressEventArgs(0, 0,
                        "Thread Aborted ... "));

                if (this.m_MustReturnRunTime)
                {
                    DateTime dtEnd = DateTime.Now;
                    TimeSpan oTimeSpan = dtEnd - m_dtStart;
                    if (localHandler != null)
                        localHandler(this, new ProgressEventArgs(100, 100,
                            "Run Time : " + oTimeSpan.ToString()));
                }
            }


        }

        public void RunUsingStreams()
        {
            m_UsingStreams = true;
            m_Thread = null;
            m_Thread = new Thread(new ThreadStart(this.ThreadFunc));
            //CreateSQLFile();
            //RemoveFile(this.GetOutputFileName());

            // start the process
            this.m_Process = null;
            m_Process = new System.Diagnostics.Process();
            // Redirect the output stream of the child process.
            m_Process.StartInfo.UseShellExecute = false;
            m_Process.EnableRaisingEvents = true;
            
            m_Process.StartInfo.RedirectStandardOutput = true;
            m_Process.OutputDataReceived += new System.Diagnostics.DataReceivedEventHandler(m_Process_OutputDataReceived);
            m_Process.StartInfo.RedirectStandardError = true;
            m_Process.ErrorDataReceived += new System.Diagnostics.DataReceivedEventHandler(m_Process_ErrorDataReceived);
            m_Process.StartInfo.CreateNoWindow = true;
            //m_Process.Exited += new EventHandler(m_Process_Exited);

            if (GetWorkingDir() != null)
                m_Process.StartInfo.WorkingDirectory = GetWorkingDir();
            m_Process.StartInfo.FileName = GetFileName();
            m_Process.StartInfo.Arguments = GetArguments();

            m_Thread.Start();
        }


        void m_Process_Exited(object sender, EventArgs e)
        {
            EventHandler<ProgressEventArgs> localHandler = ProgressEventPublisher;
            if (localHandler != null)
                localHandler(this, new ProgressEventArgs(100, 100,
                    "Process exited", true));

        }

        void m_Process_ErrorDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e)
        {
            if (e.Data != null)
            {
                EventHandler<ProgressEventArgs> localHandler = ProgressEventPublisher;
                if (localHandler != null)
                    localHandler(this, new ProgressEventArgs(0, 0,
                        e.Data, true));
            }

        }

        void m_Process_OutputDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e)
        {
            if (e.Data != null)
            {
                // check for a sql error
                bool bError = false;
                if (e.Data.Contains("Msg") && e.Data.Contains("Level") && e.Data.Contains("State"))
                    bError = true;
                EventHandler<ProgressEventArgs> localHandler = ProgressEventPublisher;
                if (localHandler != null)
                    localHandler(this, new ProgressEventArgs(0, 0,
                        e.Data, bError));
            }

        }
    }
}
